import fnmatch
import os
import shutil
import zipfile
from pathlib import Path
from util_modified_folder import find_recently_modified_folders
from util_check_folder_updated import needs_repack
from nested import flatten_to_nested_directories
import config_setting


def zip_by_level(output_path, source_path, zip_level, action, level_exclusion_path_map):
    """
    根据指定的层级对路径进行压缩打包。

    Args:
        output_path (str):
        source_path (str): 要压缩的源目录路径。
        zip_level (int): 压缩层级。0表示整体打包，n表示对第n层子目录分别打包。
        action (str): 执行具体的动作。
        level_exclusion_path_map (map): 指定每个层级的目录进行排除 e.g. 1 -> ([folder1, folder)], [file1, file2])
    """
    output_temp_path = output_path + "_temp"

    source_path = os.path.abspath(source_path)
    if not os.path.exists(source_path):
        print(f"错误：路径 '{source_path}' 不存在。")
        return

    # 计算源路径的“基础深度”，用于后续判断相对深度
    base_depth = source_path.count(os.sep)

    if zip_level == 0:
        # 层级0：整体打包
        # zip_name = f"{os.path.basename(source_path)}.zip"
        if action is None:
            pass
        elif action == "zip":
            zip_name = f"{source_path.replace(os.sep, '__').replace(':', '')}.zip"
            zip_path = os.path.join(output_temp_path, zip_name)
            zip_check_exists_path = os.path.join(output_path, zip_name).replace("__", os.sep)
            # if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or len(find_recently_modified_folders(source_path, days=config_setting.modified_threshold, include_subfolders=False)) > 0:
            if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or needs_repack(source_path, zip_check_exists_path):
                Path(os.path.dirname(zip_path)).mkdir(parents=True, exist_ok=True)
                create_zip(source_path, zip_path)
                print(f"已创建整体压缩包: {zip_name}")
            else:
                print(f"目录 {source_path} {config_setting.modified_threshold} 天未更新，跳过！")
        else:
            raise Exception(f"error: action not supported! {action}")
    else:
        # 层级n (n>=1)：遍历源路径，对符合层级要求的子目录分别打包
        for root, dirs, files in os.walk(source_path):
            dirs[:] = [d for d in dirs if not any(fnmatch.fnmatch(d, pattern) for pattern in config_setting.ignore_folder_list)]
            # # 这里是去全局目录的排除（忽略层级）
            # if exclusion_path_list:
            #     dirs[:] = [d for d in dirs if not any(fnmatch.fnmatch(d, pattern) for pattern in exclusion_paths2_list)]
            # drop_path = os.path.join(drop_path, os.path.basename(root))
            # 计算当前目录相对于源路径的深度
            # 注意：root 是当前正在访问的目录的完整路径
            current_depth = root.count(os.sep) - base_depth
            current_level = current_depth + 1
            if current_level in level_exclusion_path_map:
                filter_type = level_exclusion_path_map[current_level][0]
                if filter_type == "exclusion":
                    folder_list = level_exclusion_path_map[current_level][1]
                    dirs[:] = [d for d in dirs if not any(fnmatch.fnmatch(d, pattern) for pattern in folder_list)]
                    file_list = level_exclusion_path_map[current_level][2]
                    files[:] = [f for f in files if not any(fnmatch.fnmatch(f, pattern) for pattern in file_list)]
                elif filter_type == "inclusion":
                    folder_list = level_exclusion_path_map[current_level][1]
                    dirs[:] = [d for d in dirs if any(fnmatch.fnmatch(d, pattern) for pattern in folder_list)]
                    file_list = level_exclusion_path_map[current_level][2]
                    files[:] = [f for f in files if any(fnmatch.fnmatch(f, pattern) for pattern in file_list)]
                else:
                    raise Exception(f"error: filter_type not supported, {filter_type}")
            # relpath = os.path.join(os.path.basename(source_path), os.path.relpath(root, source_path))
            # relpath = os.path.join(os.path.splitdrive(source_path)[1].replace(os.sep, "_"), os.path.relpath(root, source_path))
            relpath = os.path.join(source_path.replace(os.sep, "__").replace(":", ""), os.path.relpath(root, source_path))
            if current_level == zip_level:
                # 这里是仅对当前目录的排除。
                # if exclusion_path_list:
                #     dirs[:] = [d for d in dirs if not any(fnmatch.fnmatch(d, pattern) for pattern in exclusion_path_list)]
                # 当达到指定层级的父级时，处理其下的直接子目录
                if action is None:
                    pass
                elif action == "zip":
                    for dir_name in dirs:
                        drop_path = os.path.join(output_temp_path, relpath)
                        subdir_path = os.path.join(root, dir_name)
                        # 压缩包名通常使用子目录名
                        zip_name = f"{dir_name}.zip"
                        zip_path = os.path.join(drop_path, zip_name)
                        zip_check_exists_path = os.path.join(output_path, relpath, zip_name).replace("__", os.sep)
                        # if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or len(find_recently_modified_folders(subdir_path, days=config_setting.modified_threshold, include_subfolders=False)) > 0:
                        if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or needs_repack(subdir_path, zip_check_exists_path):
                            Path(os.path.dirname(zip_path)).mkdir(parents=True, exist_ok=True)
                            create_zip(subdir_path, zip_path)
                            print(f"已为子目录 '{dir_name}' 创建压缩包: {zip_name}")
                        else:
                            print(f"目录 {subdir_path} {config_setting.modified_threshold} 天未更新，跳过！")

                    # 处理除子目录外的所有文件
                    if len(files) > 0:
                        drop_path = os.path.join(output_temp_path, relpath)
                        zip_name = f"base.zip"
                        zip_path = os.path.join(drop_path, zip_name)
                        zip_check_exists_path = os.path.join(output_path, relpath, zip_name).replace("__", os.sep)
                        # if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or len(find_recently_modified_folders(root, days=config_setting.modified_threshold, include_subfolders=False)) > 0:
                        if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or needs_repack(root, zip_check_exists_path):
                            Path(os.path.dirname(zip_path)).mkdir(parents=True, exist_ok=True)
                            with zipfile.ZipFile(os.path.join(drop_path, zip_name), 'w', zipfile.ZIP_DEFLATED) as zipf:
                                for file in files:
                                    file_path = os.path.join(root, file)
                                    arcname = os.path.relpath(file_path, start=root)
                                    zipf.write(file_path, arcname)
                            print(f"已为目录 '{os.path.basename(root)}' 下所有文件创建压缩包: {zip_name}")
                        else:
                            print(f"根目录 {root} {config_setting.modified_threshold} 天未更新，跳过！")
                else:
                    raise Exception(f"error: action not supported! {action}")

                # 为避免重复处理更深的目录，可以清空dirs列表
                dirs[:] = []
            elif current_level > zip_level:
                # 如果当前深度已经超过目标层级的父级，则无需继续遍历该分支
                dirs[:] = []
            else:
                # 当前目录level虽然轮空，要检查是否含有文件，如果有文件，要在当前目录做个打包
                if action is None:
                    pass
                elif action == "zip":
                    if len(files) > 0:
                        drop_path = os.path.join(output_temp_path, relpath)
                        zip_name = f"base.zip"
                        zip_path = os.path.join(drop_path, zip_name)
                        zip_check_exists_path = os.path.join(output_path, relpath, zip_name).replace("__", os.sep)
                        # if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or len(find_recently_modified_folders(root, days=config_setting.modified_threshold, include_subfolders=False)) > 0:
                        if not os.path.exists(zip_check_exists_path) or not config_setting.enable_check_modified or needs_repack(root, zip_check_exists_path):
                            Path(os.path.dirname(zip_path)).mkdir(parents=True, exist_ok=True)
                            with zipfile.ZipFile(os.path.join(drop_path, zip_name), 'w', zipfile.ZIP_DEFLATED) as zipf:
                                for file in files:
                                    file_path = os.path.join(root, file)
                                    arcname = os.path.relpath(file_path, start=root)
                                    zipf.write(file_path, arcname)
                            print(f"已为目录 '{os.path.basename(root)}' 下所有文件创建压缩包: {zip_name}")
                        else:
                            print(f"轮空目录 {root} {config_setting.modified_threshold} 天未更新，跳过！")
                else:
                    raise Exception(f"error: action not supported! {action}")
                # drop_path = os.path.join(drop_path, os.path.basename(root))

    # zip打包完成后
    if action is None:
        pass
    elif action == "zip":
        if os.path.exists(output_temp_path):  # 如果所有内容都在n天内未更新，那么xxx_temp目录就压根不存在，直接跳过这个方法即可
            flatten_to_nested_directories(output_temp_path, output_path)
            shutil.rmtree(output_temp_path)  # 删除XXX_temp文件夹
    else:
        raise Exception(f"error: action not supported! {action}")

def create_zip(source_path, zip_filepath):
    """创建ZIP压缩文件的辅助函数"""
    print(f"creating zip {zip_filepath}...")
    with zipfile.ZipFile(zip_filepath, 'w', zipfile.ZIP_DEFLATED) as zipf:
        if os.path.isfile(source_path):
            # 如果源路径是单个文件
            zipf.write(source_path, os.path.basename(source_path))
        else:
            # 如果源路径是目录，递归遍历并添加所有文件和子目录
            for root, dirs, files in os.walk(source_path):
                for file in files:
                    file_path = os.path.join(root, file)
                    # 在压缩包内创建相对路径的归档名
                    arcname = os.path.relpath(file_path, start=source_path)
                    zipf.write(file_path, arcname)